/**
 * @file lv_font_freetype.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "lv_freetype.h"
#include "lvgl/src/lv_misc/lv_debug.h"

#include "ft2build.h"
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_CACHE_H

extern void * ASR_MALLOC(unsigned long size);
/*********************
 *      DEFINES
 *********************/
#define LV_FONT_PATH_LEN      80
/**********************
 *      TYPEDEFS
 **********************/
typedef struct
{
    const char * fontpath;
    int32_t face_index;
} lv_face_id_t;

typedef struct {
    FT_Face     face;      /* handle to face object */
    uint16_t font_size;     /*font height size */
    lv_face_id_t faceid;
} lv_font_fmt_freetype_dsc_t;
/**********************
 *  STATIC PROTOTYPES
 **********************/

/**********************
*  STATIC VARIABLES
**********************/
static FT_Library library;

#if USE_CACHE_MANGER
static FTC_Manager cache_manager;
static FTC_CMapCache cmap_cache;
/*static FTC_ImageCache image_cache;*/
static FTC_SBitCache sbit_cache;
static FTC_SBit sbit;
static uint32_t lv_freetype_max_face = 2;
/**********************
 *      MACROS
 **********************/

/**********************
 *   STATIC FUNCTIONS
 **********************/
static FT_Error  font_Face_Requester(FTC_FaceID  face_id,
                                     FT_Library  ft_library,
                                     FT_Pointer  req_data,
                                     FT_Face  *  aface)
{
    lv_face_id_t * faceid = (lv_face_id_t *)face_id;
    int error;

    error = FT_New_Face( ft_library,
                         faceid->fontpath,
                         faceid->face_index,
                         aface );

    return error;
}

static bool get_glyph_dsc_cache_cb(const lv_font_t * font, lv_font_glyph_dsc_t * dsc_out, uint32_t unicode_letter, uint32_t unicode_letter_next)
{
    if(unicode_letter < 0x20) {
        dsc_out->adv_w = 0;
        dsc_out->box_h = 0;
        dsc_out->box_w = 0;
        dsc_out->ofs_x = 0;
        dsc_out->ofs_y = 0;
        dsc_out->bpp = 0;
        return true;
    }

    FT_UInt glyph_index;
    FT_UInt charmap_index;
    FT_Face face;
    lv_font_fmt_freetype_dsc_t * dsc = (lv_font_fmt_freetype_dsc_t *)(font->user_data);
    face = dsc->face;
    FTC_ImageTypeRec desc_sbit_type;

    desc_sbit_type.face_id = (FTC_FaceID)(&dsc->faceid);
    desc_sbit_type.flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL;
    desc_sbit_type.height = dsc->font_size;
    desc_sbit_type.width = 0;

    FTC_Manager_LookupFace(cache_manager, &dsc->faceid, &face);
    charmap_index = FT_Get_Charmap_Index(face->charmap);

    glyph_index = FTC_CMapCache_Lookup(cmap_cache, &dsc->faceid, charmap_index, unicode_letter);
    FTC_SBitCache_Lookup(sbit_cache, &desc_sbit_type, glyph_index, &sbit, NULL);

    dsc_out->adv_w = sbit->xadvance;
    dsc_out->box_h = sbit->height;         /*Height of the bitmap in [px]*/
    dsc_out->box_w = sbit->width;         /*Width of the bitmap in [px]*/
    dsc_out->ofs_x = sbit->left;         /*X offset of the bitmap in [pf]*/
    dsc_out->ofs_y = sbit->top - sbit->height;         /*Y offset of the bitmap measured from the as line*/
    dsc_out->bpp = 8;         /*Bit per pixel: 1/2/4/8*/

    if(sbit->buffer == NULL && unicode_letter == 0x20) {
        sbit->buffer = (FT_Byte*)ASR_MALLOC(4);
    }

    return true;                /*true: glyph found; false: glyph was not found*/
}

/* Get the bitmap of `unicode_letter` from `font`. */
static const uint8_t * get_glyph_bitmap_cache_cb(const lv_font_t * font, uint32_t unicode_letter)
{
    return (const uint8_t *)sbit->buffer;
}
#else
static bool get_glyph_dsc_cb(const lv_font_t * font, lv_font_glyph_dsc_t * dsc_out, uint32_t unicode_letter, uint32_t unicode_letter_next)
{
    if(unicode_letter < 0x20) {
        dsc_out->adv_w = 0;
        dsc_out->box_h = 0;
        dsc_out->box_w = 0;
        dsc_out->ofs_x = 0;
        dsc_out->ofs_y = 0;
        dsc_out->bpp = 0;
        return true;
    }

    int error;
    FT_Face face;
    lv_font_fmt_freetype_dsc_t * dsc = (lv_font_fmt_freetype_dsc_t *)(font->user_data);
    face = dsc->face;

    FT_UInt glyph_index = FT_Get_Char_Index(face, unicode_letter);

    error = FT_Load_Glyph(
                face,          /* handle to face object */
                glyph_index,   /* glyph index           */
                FT_LOAD_DEFAULT);   /* load flags, see below */ //FT_LOAD_MONOCHROME|FT_LOAD_NO_AUTOHINTING
    if(error) {
        printf("Error in FT_Load_Glyph: %d\n", error);
        return error;
    }

    error = FT_Render_Glyph(face->glyph,    /* glyph slot  */
                            FT_RENDER_MODE_NORMAL);  /* render mode */  //

    if(error) {
        printf("Error in FT_Render_Glyph: %d\n", error);
        return error;
    }

    dsc_out->adv_w = (face->glyph->metrics.horiAdvance >> 6);
    dsc_out->box_h = face->glyph->bitmap.rows;         /*Height of the bitmap in [px]*/
    dsc_out->box_w = face->glyph->bitmap.width;         /*Width of the bitmap in [px]*/
    dsc_out->ofs_x = face->glyph->bitmap_left;         /*X offset of the bitmap in [pf]*/
    dsc_out->ofs_y = face->glyph->bitmap_top - face->glyph->bitmap.rows;         /*Y offset of the bitmap measured from the as line*/
    dsc_out->bpp = 8;         /*Bit per pixel: 1/2/4/8*/

    return true;
}

/* Get the bitmap of `unicode_letter` from `font`. */
static const uint8_t * get_glyph_bitmap_cb(const lv_font_t * font, uint32_t unicode_letter)
{
    FT_Face face;
    lv_font_fmt_freetype_dsc_t * dsc = (lv_font_fmt_freetype_dsc_t *)(font->user_data);
    face = dsc->face;
    return (const uint8_t *)(face->glyph->bitmap.buffer);
}
#endif


/**********************
 *   GLOBAL FUNCTIONS
 **********************/

/**
* init freetype library
* @param max_faces Maximum number of opened @FT_Face objects managed by this cache
* @return FT_Error
* example: if you have two faces,max_faces should >= 2
*/
int lv_freetype_init(uint8_t max_faces)
{
    FT_Error error;
    error = FT_Init_FreeType(&library);
    if(error) {
        printf("Error in FT_Init_FreeType: %d\n", error);
        return error;
    }
#if USE_CACHE_MANGER
    //cache
    lv_freetype_max_face = max_faces;
    error = FTC_Manager_New(library, max_faces, 0, 100000, font_Face_Requester, NULL, &cache_manager);
    if(error) {
        printf("Failed to open cache manager\r\n");
        return error;
    }

    error = FTC_CMapCache_New(cache_manager, &cmap_cache);
    if(error) {
        printf("Failed to open Cmap Cache\r\n");
        return error;
    }
    /*
    error = FTC_ImageCache_New(cache_manager, &image_cache);
    if(error)
    {
        printf("Failed to open image cache\r\n");
        return error;
    }
    */
    error = FTC_SBitCache_New(cache_manager, &sbit_cache);
    if(error) {
        printf("Failed to open sbit cache\r\n");
        return error;
    }
#endif
    return FT_Err_Ok;
}

/* only for ttf debug, load ttf file to memory for test to debug ft2 file interface */
#if 0
static FT_Byte * lv_freetype_file_load(const char * path, uint32_t * size)
{
    lv_fs_file_t file;
    uint32_t file_size = *size;

    lv_fs_res_t res = lv_fs_open(&file, path, LV_FS_MODE_RD);
    if(res == LV_FS_RES_OK) {
        if(file_size == 0) {
            if(LV_FS_RES_OK != lv_fs_size(&file, &file_size)) {
                printf("[%s]: lv_fs_size failed\n", __func__);
                goto err;
            }
        }

        if(file_size <= 0) {
            printf("[%s]: lv_fs_size get size=%d wrong\n", __func__, file_size);
            goto err;
        }

        uint32_t br = 0;
        FT_Byte * buf = malloc(file_size);
        if(buf == NULL) {
            printf("[%s] No memory !!!\n", __func__);
            goto err;
        }
        memset(buf, 0, file_size);
        res = lv_fs_read(&file, buf, file_size, &br);
        *size = file_size;
        lv_fs_close(&file);
        return buf;
    } else {
        printf("[%s] error,path = %s,errno = %d !!!\n", __func__, path, res);
        return NULL;
    }

err:
    lv_fs_close(&file);
    return NULL;
}

int lv_freetype_font_init(lv_font2_t * font, const char * font_src, uint16_t font_size, bool is_file)
{
    (void)is_file;

    FT_Error error;

    lv_font_fmt_freetype_dsc_t * dsc = lv_mem_alloc(sizeof(lv_font_fmt_freetype_dsc_t));
    LV_ASSERT_MEM(dsc);
    if(dsc == NULL) return FT_Err_Out_Of_Memory;

    dsc->font_size = font_size;
    const char *font_path = font_src;

    FT_Long size = 0;
    FT_Byte *fontBuffer;

    fontBuffer = lv_freetype_file_load(font_path, (uint32_t *)&size);
    if(!fontBuffer) {
        return 1;
    }

    //printf("-------lv_freetype_font_init: size=0x%x\n", (unsigned int)size);
    //dump_buf(0x0, fontBuffer, 0x100);//huwen

    error = FT_New_Memory_Face(library, fontBuffer, size, 0, &dsc->face);
    if(error) {
        printf("Error in FT_New_Face: %d\n", error);
        return error;
    }
    error = FT_Set_Pixel_Sizes(dsc->face, 0, font_size);
    if(error) {
        printf("Error in FT_Set_Char_Size: %d\n", error);
        return error;
    }

#if USE_CACHE_MANGER
    font->get_glyph_dsc = get_glyph_dsc_cache_cb;        /*Set a callback to get info about gylphs*/
    font->get_glyph_bitmap = get_glyph_bitmap_cache_cb;  /*Set a callback to get bitmap of a glyp*/
#else
    font->get_glyph_dsc = get_glyph_dsc_cb;        /*Set a callback to get info about gylphs*/
    font->get_glyph_bitmap = get_glyph_bitmap_cb;  /*Set a callback to get bitmap of a glyp*/
#endif

    font->user_data = dsc;
    font->line_height = (dsc->face->size->metrics.height >> 6);
    font->base_line = -(dsc->face->size->metrics.descender >> 6);  /*Base line measured from the top of line_height*/
    font->subpx = LV_FONT_SUBPX_NONE;

    //free(fontBuffer);
    return FT_Err_Ok;
}
#endif

/**
* init lv_font_t struct
* @param font pointer to a font
* @param font_path the font path
* @param font_size the height of font
* @return FT_Error
*/
int lv_freetype_font_init(lv_font_t * font, const char * font_path, uint16_t font_size)
{
    FT_Error error;

    lv_font_fmt_freetype_dsc_t * dsc = lv_mem_alloc(sizeof(lv_font_fmt_freetype_dsc_t));
    LV_ASSERT_MEM(dsc);
    if(dsc == NULL) return FT_Err_Out_Of_Memory;

    dsc->faceid.fontpath = font_path;
    dsc->faceid.face_index = 0;
    error = FTC_Manager_LookupFace( cache_manager,
                                    &dsc->faceid, &dsc->face);

    if(error) {
        printf("Error in FT_New_Face: %d\n", error);
        lv_mem_free(dsc);
        return error;
    }

    dsc->font_size = font_size;
    FTC_ScalerRec scaler;
    FT_Size asize;
    scaler.face_id = (FTC_FaceID)(&dsc->faceid);
    scaler.width = 0;
    scaler.height = font_size;
    scaler.pixel = 1;
    scaler.x_res = 0;
    scaler.y_res = font_size;
    FTC_Manager_LookupSize(cache_manager, &scaler, &asize);

#if USE_CACHE_MANGER
    font->get_glyph_dsc = get_glyph_dsc_cache_cb;        /*Set a callback to get info about gylphs*/
    font->get_glyph_bitmap = get_glyph_bitmap_cache_cb;  /*Set a callback to get bitmap of a glyp*/
#else
    font->get_glyph_dsc = get_glyph_dsc_cb;        /*Set a callback to get info about gylphs*/
    font->get_glyph_bitmap = get_glyph_bitmap_cb;  /*Set a callback to get bitmap of a glyp*/
#endif

#ifndef LV_USE_USER_DATA
#error "lv_freetype : user_data is required.Enable it lv_conf.h(LV_USE_USER_DATA 1)"
#endif
    font->user_data = dsc;
    font->line_height = (asize->metrics.height >> 6);
    font->base_line = -(asize->metrics.descender >> 6);  /*Base line measured from the top of line_height*/
    font->subpx = LV_FONT_SUBPX_NONE;

    return FT_Err_Ok;
}

/**
* destroy lv_font_t struct
* @param font pointer to a font
* @return FT_Error
*/
int lv_freetype_font_destroy(lv_font_t * font)
{
    lv_font_fmt_freetype_dsc_t * dsc = (lv_font_fmt_freetype_dsc_t *)(font->user_data);
    FTC_Manager_RemoveFaceID(cache_manager, (FTC_FaceID)&(dsc->faceid));
    lv_mem_free(dsc);
    font->user_data = NULL;
    return FT_Err_Ok;
}

void lv_freetype_reset_cache(void)
{
    printf("%s\n", __FUNCTION__);
#if USE_CACHE_MANGER
    FTC_Manager_Done(cache_manager);

    //cache
    int error = FTC_Manager_New(library, lv_freetype_max_face, 0, 100000, font_Face_Requester, NULL, &cache_manager);
    if(error) {
        printf("Failed to open cache manager\r\n");
    }
    error = FTC_CMapCache_New(cache_manager, &cmap_cache);
    if(error) {
        printf("Failed to open Cmap Cache\r\n");
    }
    error = FTC_SBitCache_New(cache_manager, &sbit_cache);
    if(error) {
        printf("Failed to open sbit cache\r\n");

    }

    static FTC_CMapCache cmap_cache_last;
    if(cmap_cache_last != cmap_cache) {
        cmap_cache_last = cmap_cache;
        printf("%s %d %p\n", __FUNCTION__, __LINE__, cmap_cache);
    } else {
        printf("%s %d cmap_cache is same\n", __FUNCTION__, __LINE__);
    }

    static FTC_SBitCache sbit_cache_last;
    if(sbit_cache_last != sbit_cache) {
        sbit_cache_last = sbit_cache;
        printf("%s %d %p\n", __FUNCTION__, __LINE__, sbit_cache);
    } else {
        printf("%s %d sbit_cache is same\n", __FUNCTION__, __LINE__);
    }

    static FTC_Manager cache_manager_last;

    if(cache_manager_last != cache_manager) {
        cache_manager_last = cache_manager;
        printf("%s %d %p\n", __FUNCTION__, __LINE__, cache_manager);
    } else {
        printf("%s %d cache_manager is same\n", __FUNCTION__, __LINE__);
    }

#endif
}


/* third part libft2.a need, refer to Ftstdlib.h */

//add for third party software
/* return file instead of fd, following lv_fs */
FILE *ft2_fopen(const char *path, const char *mode)
{
  lv_fs_file_t *fp = lv_mem_alloc(sizeof(lv_fs_file_t));

  (void)mode;
  //printf("%s: file %s\n", __func__, path);

  lv_fs_res_t res = lv_fs_open(fp, path, LV_FS_MODE_RD);
  if(res != LV_FS_RES_OK) {
      printf("[%s] error,path = %s,errno = %d !!!\n", __func__, path, res);
      return NULL;
  }
  //printf ("[%s]: file =%p, path=%s\n",__FUNCTION__, fp, path);

  return (FILE*)fp;
}

int ft2_fclose(FILE *fp)
{
  lv_fs_file_t *file = (lv_fs_file_t *)fp;

  if(!file){
      printf("[%s]: file is null\n", __func__);
      return -1;
  }

  lv_res_t res = lv_fs_close(file);
  lv_mem_free(file);

  if(LV_FS_RES_OK != res) {
      printf("[%s]: failed\n", __func__);
      return -1;
  }

  //printf ("[%s]: file %p\n",__FUNCTION__, file);
  return 0;
}

size_t ft2_fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
  uint32_t rn = 0;
  lv_fs_file_t *file = (lv_fs_file_t *)stream;

  //printf("ft2_fread: file=%p, len=0x%x\n", file, (size * nmemb));
  /*lv_freetype.c FT2_read has normal case: len = 0 and buf=NULL */
  if(size * nmemb == 0 || ptr == NULL) {
      return size * nmemb;
  }

  lv_fs_read(file, ptr, (size * nmemb), &rn);

  return rn;
}

size_t ft2_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
{
  uint32_t rn = 0;
  lv_fs_file_t *file = (lv_fs_file_t *)stream;

  lv_fs_write(file, ptr, (size * nmemb), &rn);

  return rn;
}

long ft2_ftell(FILE *stream)
{
  lv_fs_file_t *file = (lv_fs_file_t *)stream;
  uint32_t pos = 0;

  lv_fs_tell(file, &pos);
  //printf("[%s]: file %p, pos=0x%x\n",__FUNCTION__, file, (unsigned)pos);

  return pos;
}

int ft2_fseek(FILE *stream, long offset, int whence)
{
  lv_fs_file_t *file = (lv_fs_file_t *)stream;

  if(LV_FS_RES_OK != lv_fs_seek_with_whence(file, offset, whence)) {
    printf("%s, failed", __func__);
    return -1;
  }
  //printf("[%s]: file =%p, offset=0x%x\n",__FUNCTION__, file, (unsigned)offset);

  return 0;
}


void ASR_FREE(void *p) {
    lv_mem_free(p);
}

void * ASR_MALLOC(unsigned long size)
{
    void * p = lv_mem_alloc(size);
    return p;
}

void * ASR_REALLOC(void *p, unsigned long size)
{
    uint32_t old_size = _lv_mem_get_size(p);
    if(old_size == size) return p; /*Also avoid reallocating the same memory*/

    void * data_p = lv_mem_alloc(size);
    if(data_p != NULL) {
        /*Copy the old data to the new. Use the smaller size*/
        if(old_size != 0) {
            _lv_memcpy(data_p, p, LV_MATH_MIN(size, old_size));
            lv_mem_free(p);
        }
    }

    return data_p;
}
